Skip to content

Add deterministic SVG degradation curve renderer#128

Merged
ProfRandom92 merged 1 commit into
mainfrom
codex/add-deterministic-svg-degradation-curve-renderer
May 19, 2026
Merged

Add deterministic SVG degradation curve renderer#128
ProfRandom92 merged 1 commit into
mainfrom
codex/add-deterministic-svg-degradation-curve-renderer

Conversation

@ProfRandom92
Copy link
Copy Markdown
Owner

Motivation

  • Provide the first deterministic benchmark visualization for layered admissibility by rendering artifacts/layered_admissibility_results.json to a stable SVG artifact.
  • Keep rendering explicit, serializable, and CI-friendly with no runtime/browser/JS dependencies and fixed layout/precision.
  • Surface failure labels and a compact legend to make deterministic degradation behavior immediately inspectable.

Description

  • Added a hand-written SVGCurveRenderer (src/visualization/svg_curve_renderer.py) that maps fixture progression (positive → mild → moderate → severe) to deterministic SVG coordinates with fixed canvas (1000×520), margins, stable ordering, and three-decimal float formatting. Changed files: src/visualization/svg_curve_renderer.py, src/visualization/__init__.py.
  • Added scripts/render_curve_svg.py to regenerate docs/media/layered_admissibility_curve.svg from the committed JSON in a CI-friendly, importable way.
  • Added deterministic tests (tests/test_svg_curve_renderer.py) that assert root SVG validity, fixture labels, presence and ordering of the expected failure annotations, monotonic plotted coordinates, stable float formatting, and exact parity with the committed SVG artifact.
  • Committed the generated artifact and docs update: docs/media/layered_admissibility_curve.svg and docs/benchmarks/layered_admissibility.md now include a Visualization section referencing the deterministic SVG.

Testing

  • Ran targeted renderer tests: pytest tests/test_svg_curve_renderer.py -q — passed (7 tests).
  • Ran full Python test suite: pytest -q — passed (184 passed).
  • Ran repository checks: npm run check — passed (layout/typecheck/validate/build/test pipeline completed).
  • Tests include an exact-regression that asserts the renderer output equals the committed SVG artifact to prevent accidental drift.

Codex Task

@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comptextv7 Ready Ready Preview, Comment May 19, 2026 11:38am

@netlify
Copy link
Copy Markdown

netlify Bot commented May 19, 2026

Deploy Preview for comptext-v7 canceled.

Name Link
🔨 Latest commit 0b81f8c
🔍 Latest deploy log https://app.netlify.com/projects/comptext-v7/deploys/6a0c4b98368b1e0008d34e99

@ProfRandom92 ProfRandom92 merged commit 728c127 into main May 19, 2026
8 of 10 checks passed
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a deterministic SVG renderer for visualizing layered admissibility degradation curves, along with a supporting CLI script and comprehensive tests. The implementation focuses on reproducibility by using fixed canvas geometry and stable float precision. Feedback was provided to improve the robustness of the renderer, including handling missing input data to avoid KeyErrors, preventing potential division-by-zero errors in coordinate calculations, and ensuring SVG background dimensions are dynamically linked to class constants. Additionally, it was suggested to include all failure labels in the visualization, even those not explicitly listed in the predefined annotation order.


layouts: list[_PointLayout] = []
for index, (fixture_id, _) in enumerate(self.X_TICKS):
point = points_by_fixture[fixture_id]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing points_by_fixture[fixture_id] directly will raise a KeyError if the input JSON is missing any of the expected benchmark fixtures defined in X_TICKS. Adding a check or a more descriptive error message would improve the robustness of the renderer, especially when used in CI environments.

Suggested change
point = points_by_fixture[fixture_id]
if fixture_id not in points_by_fixture:
raise KeyError(f"Fixture '{fixture_id}' not found in input data.")
point = points_by_fixture[fixture_id]

for index, (fixture_id, _) in enumerate(self.X_TICKS):
point = points_by_fixture[fixture_id]
score = float(point["overall_admissibility_score"])
x = self.MARGIN_LEFT + (plot_width * index / (len(self.X_TICKS) - 1))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This calculation will result in a ZeroDivisionError if X_TICKS is modified to contain only one element. Adding a guard for the denominator ensures the renderer remains robust against changes to the tick configuration.

Suggested change
x = self.MARGIN_LEFT + (plot_width * index / (len(self.X_TICKS) - 1))
x = self.MARGIN_LEFT + (plot_width * index / (len(self.X_TICKS) - 1)) if len(self.X_TICKS) > 1 else self.MARGIN_LEFT

polyline_points = " ".join(f"{self._fmt(p.x)},{self._fmt(p.y)}" for p in layouts)
elements: list[str] = [
f'<svg xmlns="http://www.w3.org/2000/svg" width="{self.WIDTH}" height="{self.HEIGHT}" viewBox="0 0 {self.WIDTH} {self.HEIGHT}">',
' <rect x="0" y="0" width="1000" height="520" fill="#ffffff"/>',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The background rectangle uses hardcoded width and height values (1000 and 520) instead of referencing the class constants WIDTH and HEIGHT. This will cause the background to be incorrectly sized if the canvas dimensions are updated in the future.

Suggested change
' <rect x="0" y="0" width="1000" height="520" fill="#ffffff"/>',
f' <rect x="0" y="0" width="{self.WIDTH}" height="{self.HEIGHT}" fill="#ffffff"/>',

Comment on lines +115 to +119
ordered_labels = [label for label in self.FAILURE_ANNOTATION_ORDER if label in point.failure_labels]
if ordered_labels:
elements.append(
f' <text x="{self._fmt(point.x)}" y="{y_base}" text-anchor="middle" font-size="10" font-family="monospace" fill="#aa2200">{", ".join(ordered_labels)}</text>'
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Failure labels that are not explicitly listed in FAILURE_ANNOTATION_ORDER are silently omitted from the visualization. To ensure all degradation information is surfaced, consider appending any unexpected labels to the end of the annotation list.

Suggested change
ordered_labels = [label for label in self.FAILURE_ANNOTATION_ORDER if label in point.failure_labels]
if ordered_labels:
elements.append(
f' <text x="{self._fmt(point.x)}" y="{y_base}" text-anchor="middle" font-size="10" font-family="monospace" fill="#aa2200">{", ".join(ordered_labels)}</text>'
)
# Prioritize labels in FAILURE_ANNOTATION_ORDER, then include any others
labels = [l for l in self.FAILURE_ANNOTATION_ORDER if l in point.failure_labels]
labels.extend(sorted(set(point.failure_labels) - set(self.FAILURE_ANNOTATION_ORDER)))
if labels:
elements.append(
f' <text x="{self._fmt(point.x)}" y="{y_base}" text-anchor="middle" font-size="10" font-family="monospace" fill="#aa2200">{", ".join(labels)}</text>'
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant